//
// Created by yunnan xu on 2020/11/30.
//

#include "Model.h"
#include "DummyModel.h"
#include "EngineCore.h"
#include <gles3/gl32.h>
#include <memory>
#include "RigidBody.h"
#include "Convex.h"

#define PI 3.1415926f

DummyModel:: ~DummyModel()
{
    for (auto kv : vertex_data)
    {
        if (kv.second)
        {
            delete [] kv.second;
        }
    }
    vertex_data.clear();
    delete [] index_data;
    index_data = nullptr;
}

template<>
std::shared_ptr<DummyModel>
DummyModel::MakeDummyModel<DummyItemType::Plane>(std::unordered_set<VertexAttribType> attrib_list, bool with_rb, float w, float h)
{
    if (attrib_list.find(VertexAttribType::POSITION_F3) == attrib_list.end())
    {
        LOGE("DummyModel MakeDummyModel Error, not contains position ... ");
        return nullptr;
    }

    auto dummy = std::make_shared<DummyModel> ();
    dummy->vertexNumber = 4;
    dummy->faceNumber = 2;
    dummy->index_type = GL_UNSIGNED_BYTE;

    // 遍历属性列表，初始化
    for (auto attrib_type : attrib_list) {

        // 这个 vertex_attrib_buffer_size 代表属性 buffer 的大小
        size_t vertex_attrib_buffer_size = dummy->vertexNumber *
                                           GLESVertexAttribToSize[(int) attrib_type] *
                                           GLESVertexAttribToCount[(int)attrib_type];
        dummy->vertex_data.emplace(attrib_type, new uint8_t[vertex_attrib_buffer_size]);
    }

    size_t index_buffer_size = sizeof(uint8_t) * dummy->faceNumber * 3;
    dummy->index_data = new uint8_t[index_buffer_size];

    float * position_data = (float *) dummy->vertex_data[VertexAttribType::POSITION_F3];
    uint8_t * index_data = dummy->index_data;

    float position[4][3] = {
            {-0.5f * w, 0.0f, 0.5f * h},
            {0.5f * w, 0.0f, 0.5f * h},
            {0.5f * w, 0.0f, -0.5f * h},
            {-0.5f * w, 0.0f, -0.5f * h},
    };

    int face_index[1][4] = {{0, 1, 2, 3},};

    float uv[4][2] = {
            {0.0f, 0.0f},
            {1.0f, 0.0f},
            {1.0f, 1.0f},
            {0.0f, 1.0f}
    };

    float nor_vec[1][3] = {
            {0.0f, 1.0f, 0.0f},
    };

    int index = 0;
    for(int f = 0; f < 1; f ++)
    {
        int p[4] = {4 * f, 4 * f + 1, 4 * f + 2, 4 * f + 3};
        for (int pindex = 0; pindex < 4; pindex ++)
            for (int i = 0; i < 3; i++)
                position_data[f * 12 + pindex * 3 + i] = position[face_index[f][pindex]][i];

        index_data[index++] = p[0]; index_data[index++] = p[1]; index_data[index++] = p[2];
        index_data[index++] = p[0]; index_data[index++] = p[2]; index_data[index++] = p[3];
    }

    // 法线
    if (attrib_list.find(VertexAttribType::NORMAL_F3) != attrib_list.end()) {

        float * normal_data = (float *) dummy->vertex_data[VertexAttribType::NORMAL_F3];
        index = 0;
        for (int f = 0; f < 1; f++)
        {
            for (int p = 0; p < 4; p ++)
                for (int i = 0; i < 3; i++)
                {
                    normal_data[index++] = nor_vec[f][i];
                }
        }
    }

    // UV
    if (attrib_list.find(VertexAttribType::UV1_F2) != attrib_list.end())
    {
        float * uv_data = (float *) dummy->vertex_data[VertexAttribType::UV1_F2];
        index = 0;
        for (int f = 0; f < 1; f++)
        {
            for (int p = 0; p < 4; p ++)
                for (int i = 0; i < 2; i++)
                {
                    uv_data[index++] = uv[p][i];
                }
        }
    }

    if (with_rb)
    {
        std::vector<glm::vec3> convex_list;

        for (int i = 0; i < 4; i ++)
        {
            convex_list.emplace_back(position[i][0], position[i][1], position[i][2]);
            convex_list.emplace_back(position[i][0], position[i][1] - 0.1f, position[i][2]);
        }

        glm::mat3x3 IBody(1.0);
        float mass = 1.0f;
        IBody[0][0] = mass / 12.0 * (w * w + h * h);
        IBody[1][1] = mass / 12.0 * (w * w);
        IBody[2][2] = mass / 12.0 * (h * h);
        dummy->rb = std::make_shared<Phy::RigidBody>(mass, IBody, std::make_shared<Phy::PolyConvex>(convex_list), dummy);
    }
    return dummy;
}

template<>
std::shared_ptr<DummyModel>
DummyModel::MakeDummyModel<DummyItemType::Triangle>(std::unordered_set<VertexAttribType> attrib_list, bool with_rb, float side_length)
{
    return NULL;
}

template<>
std::shared_ptr<DummyModel>
DummyModel::MakeDummyModel<DummyItemType::Box>(std::unordered_set<VertexAttribType> attrib_list, bool with_rb, float l, float w, float h)
{
    if (attrib_list.find(VertexAttribType::POSITION_F3) == attrib_list.end())
    {
        LOGE("DummyModel MakeDummyModel Error, not contains position ... ");
        return nullptr;
    }

    auto dummy = std::make_shared<DummyModel> ();
    dummy->vertexNumber = 24;
    dummy->faceNumber = 12;
    dummy->index_type = GL_UNSIGNED_BYTE;

    // 遍历属性列表，初始化
    for (auto attrib_type : attrib_list) {

        // 这个 vertex_attrib_buffer_size 代表属性 buffer 的大小
        size_t vertex_attrib_buffer_size = dummy->vertexNumber *
                                           GLESVertexAttribToSize[(int) attrib_type] *
                                           GLESVertexAttribToCount[(int)attrib_type];
        dummy->vertex_data.emplace(attrib_type, new uint8_t[vertex_attrib_buffer_size]);
    }

    size_t index_buffer_size = sizeof(uint8_t) * dummy->faceNumber * 3;
    dummy->index_data = new uint8_t[index_buffer_size];

    float * position_data = (float *) dummy->vertex_data[VertexAttribType::POSITION_F3];
    uint8_t * index_data = dummy->index_data;

    float position[8][3] = {
            {-0.5f * l, -0.5f * h, 0.5f * w},
            {0.5f * l, -0.5f * h, 0.5f * w},
            {0.5f * l, -0.5f * h, -0.5f * w},
            {-0.5f * l, -0.5f * h, -0.5f * w},
            {-0.5f * l, 0.5f * h, 0.5f * w},
            {0.5f * l, 0.5f * h, 0.5f * w},
            {0.5f * l, 0.5f * h, -0.5f * w},
            {-0.5f * l, 0.5f * h, -0.5f * w},
    };

    int face_index[6][4] = {{1, 0, 3, 2},
                            {5, 1, 2, 6},
                            {6, 2, 3, 7},
                            {7, 3, 0, 4},
                            {7, 4, 5, 6},
                            {4, 0, 1, 5}};

    float uv[4][2] = {
        {0.0f, 0.0f},
        {1.0f, 0.0f},
        {1.0f, 1.0f},
        {0.0f, 1.0f}
    };

    float nor_vec[6][3] = {
            {0.0f, -1.0f, 0.0f},
            {1.0f, 0.0f, 0.0f},
            {0.0f, 0.0f, -1.0f},
            {-1.0f, 0.0f, 0.0f},
            {0.0f, 1.0f, 0.0f},
            {0.0f, 0.0f, 1.0f},
    };

    int index = 0;
    for(int f = 0; f < 6; f ++)
    {
        int p[4] = {4 * f, 4 * f + 1, 4 * f + 2, 4 * f + 3};
        for (int pindex = 0; pindex < 4; pindex ++)
            for (int i = 0; i < 3; i++)
                position_data[f * 12 + pindex * 3 + i] = position[face_index[f][pindex]][i];

        index_data[index++] = p[0]; index_data[index++] = p[1]; index_data[index++] = p[2];
        index_data[index++] = p[0]; index_data[index++] = p[2]; index_data[index++] = p[3];
    }

    // 法线
    if (attrib_list.find(VertexAttribType::NORMAL_F3) != attrib_list.end()) {

        float * normal_data = (float *) dummy->vertex_data[VertexAttribType::NORMAL_F3];
        index = 0;
        for (int f = 0; f < 6; f++)
        {
            for (int p = 0; p < 4; p ++)
                for (int i = 0; i < 3; i++)
                {
                    normal_data[index++] = nor_vec[f][i];
                }
        }
    }

    // UV
    if (attrib_list.find(VertexAttribType::UV1_F2) != attrib_list.end())
    {
        float * uv_data = (float *) dummy->vertex_data[VertexAttribType::UV1_F2];
        index = 0;
        for (int f = 0; f < 6; f++)
        {
            for (int p = 0; p < 4; p ++)
                for (int i = 0; i < 2; i++)
                {
                    uv_data[index++] = uv[p][i];
                }
        }
    }


    if (with_rb)
    {
        std::vector<glm::vec3> convex_list;

        for (int i = 0; i < 8; i ++)
        {
            convex_list.emplace_back(position[i][0], position[i][1], position[i][2]);
        }

        glm::mat3x3 IBody(1.0);
        float mass = 1.0f;
        IBody[0][0] = mass / 12.0 * (w * w + h * h);
        IBody[1][1] = mass / 12.0 * (l * l + w * w);
        IBody[2][2] = mass / 12.0 * (l * l + h * h);
        dummy->rb = std::make_shared<Phy::RigidBody>(mass, IBody, std::make_shared<Phy::PolyConvex>(convex_list), dummy);
    }

    return dummy;
}

/*
 * 使用二十面体的球形mesh生成算法
 * 在直角坐标系中，一个边长为2、重心在原点的正二十面体的坐标分别为
        (0, ±1, ±φ)
        (±1, ±φ, 0)
        (±φ, 0, ±1)
        其中φ = (1 + 5^0.5) * 0.5
        是黄金比例


 *
 * */

template<>
std::shared_ptr<DummyModel>
DummyModel::MakeDummyModel<DummyItemType::Sphere>(std::unordered_set<VertexAttribType> attrib_list, bool with_rb, float r, int warp_number)
{
    if (attrib_list.find(VertexAttribType::POSITION_F3) == attrib_list.end())
    {
        LOGE("DummyModel MakeDummyModel Error, not contains position ... ");
        return nullptr;
    }

    auto dummy = std::make_shared<DummyModel> ();

    dummy->vertexNumber = 24;
    dummy->faceNumber = 12;
    dummy->index_type = GL_UNSIGNED_INT;

    // 正二十面体的骨架
    float icosahedron_vertex_spherical_coord[12][3];   // (r, theta, phi) 球坐标系
    // int icosahedron_face[20][3];
    float theta1 = PI * 0.5f - atan(0.5f);
    float theta2 = PI * 0.5f + atan(0.5f);

    icosahedron_vertex_spherical_coord[0][0] = r;
    icosahedron_vertex_spherical_coord[0][1] = 0;
    icosahedron_vertex_spherical_coord[0][2] = 0;

    for (int i = 1; i <= 5; i ++)
    {
        icosahedron_vertex_spherical_coord[i][0] = r;
        icosahedron_vertex_spherical_coord[i][1] = theta1;
        icosahedron_vertex_spherical_coord[i][2] = ((float)i * 72.0f) / 180.0f * PI;
    }

    for (int i = 6; i <= 10; i ++)
    {
        icosahedron_vertex_spherical_coord[i][0] = r;
        icosahedron_vertex_spherical_coord[i][1] = theta2;
        icosahedron_vertex_spherical_coord[i][2] = ((float)i * 72.0f + 36.0f) / 180.0f * PI;
    }

    icosahedron_vertex_spherical_coord[11][0] = r;
    icosahedron_vertex_spherical_coord[11][1] = -PI;
    icosahedron_vertex_spherical_coord[11][2] = 0;

    return NULL;
}

template<>
std::shared_ptr<DummyModel>
DummyModel::MakeDummyModel<DummyItemType::Icosahedron>(std::unordered_set<VertexAttribType> attrib_list, bool with_rb, float r)
{
    if (attrib_list.find(VertexAttribType::POSITION_F3) == attrib_list.end())
    {
        LOGE("DummyModel MakeDummyModel Error, not contains position ... ");
        return nullptr;
    }

    auto dummy = std::make_shared<DummyModel> ();

    dummy->vertexNumber = 12;
    dummy->faceNumber = 20;
    dummy->index_type = GL_UNSIGNED_INT;

    // 遍历属性列表，初始化
    for (auto attrib_type : attrib_list) {

        // 这个 vertex_attrib_buffer_size 代表属性 buffer 的大小
        size_t vertex_attrib_buffer_size = dummy->vertexNumber *
                                           GLESVertexAttribToSize[(int) attrib_type] *
                                           GLESVertexAttribToCount[(int)attrib_type];
        dummy->vertex_data.emplace(attrib_type, new uint8_t[vertex_attrib_buffer_size]);
    }

    size_t index_buffer_size = sizeof(uint8_t) * dummy->faceNumber * 3;
    dummy->index_data = (uint8_t *)(new uint32_t[index_buffer_size]);

    float * position_data = (float *) dummy->vertex_data[VertexAttribType::POSITION_F3];
    uint32_t * index_data = (uint32_t *) dummy->index_data;

    // 正二十面体的骨架
    float icosahedron_vertex_spherical_coord[12][3];   // (r, theta, phi) 球坐标系
    int icosahedron_face[20][3];
    float theta1 = PI * 0.5f - atan(0.5f);
    float theta2 = PI * 0.5f + atan(0.5f);

//    float theta1 = atan(0.5f);
//    float theta2 = PI - atan(0.5f);

    icosahedron_vertex_spherical_coord[0][0] = r;
    icosahedron_vertex_spherical_coord[0][1] = 0;
    icosahedron_vertex_spherical_coord[0][2] = 0;

    for (int i = 1; i <= 5; i ++)
    {
        icosahedron_vertex_spherical_coord[i][0] = r;
        icosahedron_vertex_spherical_coord[i][1] = theta1;
        icosahedron_vertex_spherical_coord[i][2] = ((float)i * 72.0f) / 180.0f * PI;
    }

    for (int i = 6; i <= 10; i ++)
    {
        icosahedron_vertex_spherical_coord[i][0] = r;
        icosahedron_vertex_spherical_coord[i][1] = theta2;
        icosahedron_vertex_spherical_coord[i][2] = ((float)i * 72.0f + 36.0f) / 180.0f * PI;
    }

    icosahedron_vertex_spherical_coord[11][0] = r;
    icosahedron_vertex_spherical_coord[11][1] = -PI;
    icosahedron_vertex_spherical_coord[11][2] = 0;

    icosahedron_face[0][0] = 0;  icosahedron_face[0][2] = 2; icosahedron_face[0][1] = 1;
    icosahedron_face[1][0] = 0;  icosahedron_face[1][2] = 3; icosahedron_face[1][1] = 2;
    icosahedron_face[2][0] = 0;  icosahedron_face[2][2] = 4; icosahedron_face[2][1] = 3;
    icosahedron_face[3][0] = 0;  icosahedron_face[3][2] = 5; icosahedron_face[3][1] = 4;
    icosahedron_face[4][0] = 0;  icosahedron_face[4][2] = 1; icosahedron_face[4][1] = 5;

    icosahedron_face[5][0] = 6;  icosahedron_face[5][2] = 1; icosahedron_face[5][1] = 2;
    icosahedron_face[6][0] = 7;  icosahedron_face[6][2] = 2; icosahedron_face[6][1] = 3;
    icosahedron_face[7][0] = 8;  icosahedron_face[7][2] = 3; icosahedron_face[7][1] = 4;
    icosahedron_face[8][0] = 9;  icosahedron_face[8][2] = 4; icosahedron_face[8][1] = 5;
    icosahedron_face[9][0] = 10;  icosahedron_face[9][2] = 5; icosahedron_face[9][1] = 1;

    icosahedron_face[10][0] = 2;  icosahedron_face[10][2] = 7; icosahedron_face[10][1] = 6;
    icosahedron_face[11][0] = 3;  icosahedron_face[11][2] = 8; icosahedron_face[11][1] = 7;
    icosahedron_face[12][0] = 4;  icosahedron_face[12][2] = 9; icosahedron_face[12][1] = 8;
    icosahedron_face[13][0] = 5;  icosahedron_face[13][2] = 10; icosahedron_face[13][1] = 9;
    icosahedron_face[14][0] = 1;  icosahedron_face[14][2] = 6; icosahedron_face[14][1] = 10;

    icosahedron_face[15][0] = 11;  icosahedron_face[15][2] = 6; icosahedron_face[15][1] = 7;
    icosahedron_face[16][0] = 11;  icosahedron_face[16][2] = 7; icosahedron_face[16][1] = 8;
    icosahedron_face[17][0] = 11;  icosahedron_face[17][2] = 8; icosahedron_face[17][1] = 9;
    icosahedron_face[18][0] = 11;  icosahedron_face[18][2] = 9; icosahedron_face[18][1] = 10;
    icosahedron_face[19][0] = 11;  icosahedron_face[19][2] = 10; icosahedron_face[19][1] = 6;

    int count = 0;
    for (int i = 0; i < 12; i ++)
    {
        float small_r = r * sin(icosahedron_vertex_spherical_coord[i][1]);
        position_data[count ++] = small_r * sin(icosahedron_vertex_spherical_coord[i][2]);
        position_data[count ++] = r * cos(icosahedron_vertex_spherical_coord[i][1]);
        position_data[count ++] = small_r * cos(icosahedron_vertex_spherical_coord[i][2]);;
    }

    count = 0;
    for (int i = 0; i < 20; i ++)
    {
        for (int j = 0; j < 3; j ++)
        {
            index_data[count ++] = icosahedron_face[i][j];
        }
    }

    // 法线
    if (attrib_list.find(VertexAttribType::NORMAL_F3) != attrib_list.end()) {

        float *normal_data = (float *) dummy->vertex_data[VertexAttribType::NORMAL_F3];
        for (int index = 0; index < 12 * 3 ; index++) {
            normal_data[index] = position_data[index] / r;
        }
    }

    // UV
    if (attrib_list.find(VertexAttribType::UV1_F2) != attrib_list.end())
    {
        float * uv_data = (float *) dummy->vertex_data[VertexAttribType::UV1_F2];

        int index = 0;
        for (int i = 0; i < 12; i++)
        {
            uv_data[index ++ ] = (float) ((i + 1) % 6) / 6.0f;
            uv_data[index ++ ] = (float) ((i + 1) % 5) / 5.0f;
        }
    }

    return dummy;
}


int DummyModel::GetVertexNumber() const
{
    return vertexNumber;
}

int DummyModel::GetFaceNumber() const
{
    return faceNumber;
}

const void * DummyModel::GetVertexData(VertexAttribType attrib_type) const
{
    auto vertex_data_pair = vertex_data.find(attrib_type);
    if (vertex_data_pair == vertex_data.end())
    {
        return nullptr;
    }
    return (const void *) vertex_data_pair->second;
}


size_t DummyModel::GetVertexDataLength(VertexAttribType attrib_type) const
{
    return vertexNumber * GLESVertexAttribToSize[(int)attrib_type] * GLESVertexAttribToCount[(int)attrib_type];
}

size_t DummyModel::GetIndexDataLength() const
{
    if (index_type == GL_UNSIGNED_BYTE)
    {
        return sizeof(uint8_t) * faceNumber * 3;
    }
    else if (index_type == GL_UNSIGNED_SHORT)
    {
        return sizeof(uint16_t) * faceNumber * 3;
    }
    else if (index_type == GL_UNSIGNED_INT)
    {
        return sizeof(uint32_t) * faceNumber * 3;
    }
    else{
        return 0;
    }
}

const void * DummyModel::GetIndexData() const
{
    return (const void *) index_data;
}


GLenum DummyModel::GetIndexType() const
{
    return index_type;
}